package cn.juque.jdfsystem.api;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.juque.common.base.BaseRequestVO;
import cn.juque.common.base.BaseResponseVO;
import cn.juque.common.constants.MessageEnum;
import cn.juque.common.constants.StringConstants;
import cn.juque.common.exception.AppException;
import cn.juque.common.fastjson.abstracts.AbstractTypeReference;
import cn.juque.common.utils.HttpClientUtil;
import cn.juque.common.utils.dto.GetDTO;
import cn.juque.common.utils.dto.PostDTO;
import cn.juque.common.utils.dto.UploadDTO;
import cn.juque.jdfsystem.dto.FileInfoDetailDTO;
import cn.juque.jdfsystem.dto.FileTrustParamDTO;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * @author juque
 * @version 1.0.0
 * <ul>
 *     <li>FileInfoClient</li>
 * </ul>
 * @date 2023-01-13 14:20:20
 **/
@Slf4j
public class FileInfoClient {

    public static final String SIGNATURE = "signature";

    public static final String H_MAC_KEY = "jdf-system-2023";

    private static final HMac H_MAC = new HMac(HmacAlgorithm.HmacMD5, H_MAC_KEY.getBytes(StandardCharsets.UTF_8));

    /**
     * 节点IP列表
     */
    private final List<String> ipList;

    /**
     * 平台ID
     */
    private final String platformId;

    /**
     * 凭据
     */
    private final String secret;

    public FileInfoClient(List<String> ipList, String platformId, String secret) {
        Assert.notEmpty(ipList);
        Assert.notEmpty(platformId);
        Assert.notEmpty(secret);
        this.ipList = ipList;
        this.platformId = platformId;
        this.secret = secret;
    }

    /**
     * 确认文件状态
     *
     * @param validFileIdList   可见的文件列表
     * @param inValidFileIdList 不可见的文件列表
     * @return true=成功；false=失败
     */
    public Boolean trustFile(List<String> validFileIdList, List<String> inValidFileIdList) {
        long startTime = System.currentTimeMillis();
        FileTrustParamDTO paramDTO = new FileTrustParamDTO();
        paramDTO.setValidFileIdList(validFileIdList);
        paramDTO.setInValidFileIdList(inValidFileIdList);
        BaseRequestVO<FileTrustParamDTO> requestVO = new BaseRequestVO<>();
        requestVO.setParam(paramDTO);
        Map<String, String> headers = this.createHeaders();
        PostDTO postDTO = new PostDTO(createTrustUrl(), headers, requestVO, null);
        BaseResponseVO<Boolean> responseVO = HttpClientUtil.post(postDTO, new AbstractTypeReference<BaseResponseVO<Boolean>>() {
        });
        log.info("FileInfoClient.trustFile cost {}ms", System.currentTimeMillis() - startTime);
        return MessageEnum.OK.getCode().equals(responseVO.getCode());
    }

    /**
     * 上传文件
     *
     * @param fileName  文件名称
     * @param fileBytes 文件流
     * @return 文件ID
     */
    public String upload(String fileName, byte[] fileBytes) {
        Assert.notEmpty(fileName);
        Assert.notNull(fileBytes);
        long startTime = System.currentTimeMillis();
        UploadDTO uploadDTO = new UploadDTO();
        uploadDTO.setUrl(this.createUploadUrl());
        uploadDTO.setHeaders(this.createHeaders());
        uploadDTO.setFileKey("file");
        uploadDTO.setByteArray(fileBytes);
        uploadDTO.setFileName(fileName);
        Map<String, String> paramMap = MapUtil.newHashMap(1);
        paramMap.put(StringConstants.PLATFORM_ID, this.platformId);
        uploadDTO.setParams(paramMap);
        BaseResponseVO<String> responseVO =
                HttpClientUtil.upload(uploadDTO, new AbstractTypeReference<BaseResponseVO<String>>() {
                });
        if (!MessageEnum.OK.getCode().equals(responseVO.getCode())) {
            throw new AppException(MessageEnum.SYSTEM_FAIL.getCode(), "文件上传失败");
        }
        log.info("FileInfoClient.upload cost {}ms", System.currentTimeMillis() - startTime);
        return responseVO.getData();
    }

    /**
     * 下载文件
     *
     * @param fileId 文件ID
     * @return DownloadDTO
     */
    public byte[] download(String fileId) {
        Assert.notEmpty(fileId);
        GetDTO getDTO = new GetDTO(this.createDownloadUrl(fileId), MapUtil.newHashMap(), null);
        return HttpClientUtil.download(getDTO);
    }

    /**
     * 获取文件信息
     *
     * @param fileIdList 文件id列表
     * @return List
     */
    public List<FileInfoDetailDTO> listFileInfo(List<String> fileIdList) {
        BaseRequestVO<List<String>> requestVO = new BaseRequestVO<>();
        requestVO.setParam(fileIdList);
        PostDTO postDTO = new PostDTO(this.createFileInfoUrl(), this.createHeaders(), requestVO, null);
        BaseResponseVO<List<FileInfoDetailDTO>> responseVO = HttpClientUtil.post(postDTO,
                new AbstractTypeReference<BaseResponseVO<List<FileInfoDetailDTO>>>() {
                });
        if (!MessageEnum.OK.getCode().equals(responseVO.getCode())) {
            throw new AppException(responseVO.getCode(), responseVO.getMsg());
        }
        return responseVO.getData();
    }

    private String createTrustUrl() {
        int max = this.ipList.size() > 1 ? this.ipList.size() - 1 : 1;
        int index = RandomUtil.randomInt(0, max);
        return StringConstants.HTTP + ipList.get(index) + "/jdf/api/file/trustFile";
    }

    private String createUploadUrl() {
        int max = this.ipList.size() > 1 ? this.ipList.size() - 1 : 1;
        int index = RandomUtil.randomInt(0, max);
        return StringConstants.HTTP + ipList.get(index) + "/jdf/api/file/upload";
    }

    private String createDownloadUrl(String fileId) {
        int max = this.ipList.size() > 1 ? this.ipList.size() - 1 : 1;
        int index = RandomUtil.randomInt(0, max);
        return StringConstants.HTTP + ipList.get(index) + "/jdf/file/view/" + fileId;
    }

    private String createFileInfoUrl() {
        int max = this.ipList.size() > 1 ? this.ipList.size() - 1 : 1;
        int index = RandomUtil.randomInt(0, max);
        return StringConstants.HTTP + ipList.get(index) + "/jdf/api/file/listFileInfo";
    }

    private String createSignature() {
        return H_MAC.digestHex(this.secret);
    }

    private Map<String, String> createHeaders() {
        Map<String, String> headers = MapUtil.newHashMap();
        headers.put(StringConstants.PLATFORM_ID, this.platformId);
        headers.put(SIGNATURE, this.createSignature());
        return headers;
    }
}
